#include "StdAfx.h"
#include "RichEdit.h"

std::map<HWND, CErrorScreen*> gDialogs;

int __stdcall CErrorScreen::DialogProc(HWND hWindow, UINT uMsg, WPARAM w, LPARAM l)
{
	switch(uMsg)
	{
	case WM_CLOSE:
		{
			if(gDialogs[hWindow])
				gDialogs[hWindow]->m_running = false;
			PostQuitMessage(0);
			return TRUE;
		}
	case WM_DESTROY:
		{
			return TRUE;
		}
	case WM_COMMAND:
		{
			if(LOWORD(w) == IDC_BUTTON1)
			{
				gDialogs[hWindow]->m_running = false;
				PostQuitMessage(0);
			}
			break;
		}
	}
	return FALSE;
}

CErrorScreen::CErrorScreen()
{
	m_hDialog = CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_FORMVIEW), GetDesktopWindow(), &CErrorScreen::DialogProc, (LPARAM)this);
	m_hCrashd = GetDlgItem(m_hDialog, IDC_CHECK1);
	m_hContinue = GetDlgItem(m_hDialog, IDC_CHECK2);
	m_hSave = GetDlgItem(m_hDialog, IDC_CHECK3);
	m_hRichEdit = GetDlgItem(m_hDialog, IDC_RICHEDIT21);
	SendMessage(m_hCrashd, BM_SETCHECK, BST_CHECKED, 0);
	SendMessage(m_hSave, BM_SETCHECK, BST_CHECKED, 0);
	gDialogs[m_hDialog] = this;
	WINDOWINFO inf;
	GetWindowInfo(m_hDialog, &inf);
	RECT r;
	GetClientRect(GetDesktopWindow(), &r);
	SetWindowPos(m_hDialog, HWND_TOP, (r.right - r.left) / 2 - (inf.rcClient.right - inf.rcClient.left) / 2,
		(r.bottom - r.top) / 2 - (inf.rcClient.bottom - inf.rcClient.top) / 2, 0, 0, SWP_NOSIZE);
}

void CErrorScreen::ShowDialog()
{
	m_running = true;
	ShowWindow(m_hDialog, SW_SHOW);
	while(m_running)
	{
		MSG msg;
		while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			if(!IsDialogMessage(m_hDialog, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		Sleep(10);
	}
	ShowWindow(m_hDialog, SW_HIDE);
}

void CErrorScreen::HandleError(ui32 excode, EXCEPTION_POINTERS* ep)
{
	std::string exception = "Unknown";
	switch(excode)
	{
	case EXCEPTION_ACCESS_VIOLATION:
		exception = "Access violation";
		break;
	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
		exception = "Array bounds exceeded";
		break;
	case EXCEPTION_BREAKPOINT:
		exception = "Breakpoint triggered";
		break;
	case EXCEPTION_DATATYPE_MISALIGNMENT:
		exception = "Read/Write on misalinged data";
		break;
	case EXCEPTION_FLT_DENORMAL_OPERAND:
		exception = "Denormalized floating point operand";
		break;
	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		exception = "Floating point division by zero";
		break;
	case EXCEPTION_FLT_INEXACT_RESULT:
		exception = "Inexact floating point operation";
		break;
	case EXCEPTION_FLT_INVALID_OPERATION:
		exception = "Invalid floating point operation";
		break;
	case EXCEPTION_FLT_OVERFLOW:
		exception = "Floating point operation overflow";
		break;
	case EXCEPTION_FLT_STACK_CHECK:
		exception = "Stack corruption (floating point)";
		break;
	case EXCEPTION_FLT_UNDERFLOW:
		exception = "Floating point operation underflow";
		break;
	case EXCEPTION_GUARD_PAGE:
		exception = "Access on guarded pages";
		break;
	case EXCEPTION_ILLEGAL_INSTRUCTION:
		exception = "Illegal thread instruction";
		break;
	case EXCEPTION_IN_PAGE_ERROR:
		exception = "Unreachable page";
		break;
	case EXCEPTION_INT_DIVIDE_BY_ZERO:
		exception = "Integer division by zero";
		break;
	case EXCEPTION_INT_OVERFLOW:
		exception = "Integer overflow";
		break;
	case EXCEPTION_INVALID_DISPOSITION:
		exception = "Invalid Disposition (this cannot happen!)";
		break;
	case EXCEPTION_INVALID_HANDLE:
		exception = "Invalid kernel object";
		break;
	case EXCEPTION_NONCONTINUABLE_EXCEPTION:
		exception = "Continue after a critical exception";
		break;
	case EXCEPTION_PRIV_INSTRUCTION:
		exception = "Operation not allowed";
		break;
	case EXCEPTION_SINGLE_STEP:
		exception = "Trace trap signal";
		break;
	case EXCEPTION_STACK_OVERFLOW:
		exception = "Stack overflow";
		break;
	}
	char pMessage[4096];
	sprintf_s<4096>(pMessage, "Exception Code: %s (0x%X)\n\n", exception.c_str(), excode);
	std::string szNText = pMessage;
	sprintf_s<4096>(pMessage, "An exception occured at location '0x%08X'...\n\n", ep->ExceptionRecord->ExceptionAddress);
	szNText += pMessage;
	if(excode == EXCEPTION_ACCESS_VIOLATION)
	{
		sprintf_s<4096>(pMessage, "The thread %u tried to %s data at location '0x%08X'!\n\n", GetCurrentThreadId(),
			ep->ExceptionRecord->ExceptionInformation[0] == 8 ? "DEP" : ep->ExceptionRecord->ExceptionInformation[0] ? "write" : "read",
			ep->ExceptionRecord->ExceptionInformation[1]);
		szNText += pMessage;
	}
	szNText += "Registers:\n";
	PCONTEXT pC = ep->ContextRecord;
	sprintf_s<4096>(pMessage, "EAX: 0x%08X\tEBX: 0x%08X\tECX: 0x%08X\n", pC->Eax, pC->Ebx, pC->Ecx);
	szNText += pMessage;
	sprintf_s<4096>(pMessage, "EDX: 0x%08X\tEBP: 0x%08X\tEDI: 0x%08X\n", pC->Edx, pC->Ebp, pC->Edi);
	szNText += pMessage;
	sprintf_s<4096>(pMessage, "EIP: 0x%08X\tESI: 0x%08X\tESP: 0x%08X\n\n", pC->Eip, pC->Esi, pC->Esp);
	szNText += pMessage;

	szNText += "Additional information:\n";
	if(ep->ExceptionRecord->ExceptionFlags == 0)
	{
		EnableWindow(m_hContinue, TRUE);
		szNText += "It is possible to continue execution after this exception. This of course doesnt guarantee that the program will\ncontinue working.\n\n";
	}
	else
	{
		SendMessage(m_hCrashd, BM_SETCHECK, BST_UNCHECKED, 0);
		EnableWindow(m_hContinue, FALSE);
		szNText += "It is not possible to continue execution after this exception. Any attempts will result in another exception\nimmediatly.\n\n";
	}
	Vertex3F cam = sCamera.GetCamPosition();
	cam.x = ZEROPOINT - cam.x;
	cam.z = ZEROPOINT - cam.z;
	sprintf_s<4096>(pMessage, "Current position:\nClient: %f %f %f\nServer: %f %f %f\n\n", sCamera.GetCamPosition().x,
		sCamera.GetCamPosition().y, sCamera.GetCamPosition().z, cam.x, cam.z, cam.y);
	szNText += pMessage;
	szNText += "Configuration:\n";
	szNText += "Locale: ";
	szNText += sMpqHandler.GetLocale() + "\n\n";
	DWORD dwNeeded = 0;
	HMODULE dest;
	EnumProcessModules(GetCurrentProcess(), &dest, sizeof(HMODULE), &dwNeeded);
	if(dwNeeded >= sizeof(HMODULE))
	{
		HMODULE* pDest = new HMODULE[dwNeeded / sizeof(HMODULE)];
		EnumProcessModules(GetCurrentProcess(), pDest, dwNeeded, &dwNeeded);
		ui32 numModules = dwNeeded / sizeof(HMODULE);
		if(numModules)
		{
			szNText += "Loaded modules:\n";
			for(ui32 i = 0; i < numModules; ++i)
			{
				CHAR szFileName[MAX_PATH] = { 0 };
				GetModuleFileName(pDest[i], szFileName, MAX_PATH);
				szNText += szFileName;
				szNText += "\n";
			}
		}
		delete [] pDest;
	}
	AppendText(szNText);
}

void CErrorScreen::AppendText(std::string szText)
{
	GETTEXTEX gt;
	gt.cb = 4096;
	gt.codepage = 0;
	gt.flags = GT_DEFAULT;
	gt.lpDefaultChar = 0;
	gt.lpUsedDefChar = 0;
	char* pText = new char[16384];
	SendMessage(m_hRichEdit, EM_GETTEXTEX, (WPARAM)&gt, (LPARAM)pText);
	std::string nText = pText;
	delete [] pText;
	nText += szText;
	SETTEXTEX st;
	st.codepage = 0;
	st.flags = GT_DEFAULT;
	SendMessage(m_hRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)nText.c_str());
}

ui32 CErrorScreen::RaiseException(ui32 excode, EXCEPTION_POINTERS* ep, const char* info)
{
	CErrorScreen sc;
	SetWindowText(sc.m_hDialog, info);
	sc.HandleError(excode, ep);
	sc.ShowDialog();
	if(sc.IsCrashDumpEnabled())
	{
		MINIDUMP_EXCEPTION_INFORMATION info;
		info.ClientPointers = FALSE;
		info.ExceptionPointers = ep;
		info.ThreadId = GetCurrentThreadId();
		CreateDirectory("Crashdumps", 0);
		char path[MAX_PATH];
		time_t t = time(0);
		tm* tM = localtime(&t);
		sprintf(path, "Crashdumps\\Dump~%02u_%02u_%02u_%02u(%02u-%02u-%04u).dmp", tM->tm_hour, tM->tm_min, tM->tm_sec, 
			t, tM->tm_mday, tM->tm_mon + 1, tM->tm_year + 1900);
		HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
		MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithIndirectlyReferencedMemory, &info, 0, 0);
		CloseHandle(hFile);
	}
	if(sc.IsSaveEnabled())
	{
		__try
		{
			sD3D.Save();
		}
		__except(1)
		{
		}
	}
	if(!sc.IsContinueEnabled())
	{
		sWindow.Destroy();
	}
	sc.Destroy();
	return 1;
}